<?php

namespace Fluency;

/*
 * @package Fluency
 * @author  Ignas Rudaitis
 * @version 2.0.0
 */
class Filterer
{
    private $filterLoader;
    private $filters;
    private $argumentMappings;

    public function __construct($filterLoader)
    {
        if (!is_callable($filterLoader)) {
            throw new \BadFunctionCallException("The filter loader is not callable");
        }
        $this->filterLoader = $filterLoader;
    }

    private function getFilter($name)
    {
        $filter = &$this->filters[$name];
        if (!isset($filter)) {
            $loader = $this->filterLoader;
            list($filter, $this->argumentMappings[$name]) = $loader($name);
        }
        return $filter;
    }

    public function applyFilters(array $filters, Injector $injector)
    {
        $continuations = array();
        foreach ($filters as $name => $parameters) {
            $filter = $this->getFilter($name);
            $mapping = $this->argumentMappings[$name];
            $result = $injector->withArrayContainer($parameters)->call($filter, $mapping);
            $this->handleFilterResult($result, $continuations, $name);
        }
        return array_reverse($continuations, true);
    }

    private function handleFilterResult($result, &$continuations, $filterName)
    {
        if (is_callable($result)) {
            $continuations[$filterName] = $result;
        } elseif ($result !== null) {
            throw new \UnexpectedValueException(
                    "Filter '$filterName' returned a non-callable and non-null value");
        }
    }

    public function applyContinuations(array $continuations, Response $response)
    {
        foreach ($continuations as $filterName => $continuation) {
            $response = $continuation($response);
            if (!$response instanceof Response) {
                throw new \UnexpectedValueException(
                        "The continuation of filter '$filterName' did not return a Response");
            }
        }
        return $response;
    }
}
